﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Motoball.screenmanager;
using Motoball.screens;

namespace Motoball.Paddles
{//memo:check if works correctly after conversion from 2D to 3D
    public class AIPlayer : Paddle
    {
        //rotation to place where the ball hits the inner radius
        float targetRotation = 0.0f;
        public bool ballHunting = true;

        public AIPlayer(Color color, GameplayScreen screen)
            : base(color, screen, 1)
        {
            
        }

        #region Properties
        public float TargetRotation
        {
            set { targetRotation = value; }
        }
        #endregion
        #region Methods
       

        //indicates strength of AI - higher values result in a meaner AI (see RandomAggressiveness() )
        public int aiStrength = 0;
        float maxDelta = 0;
        float distanceDelta = 0;
        float lastDistance = 0;
        enum Aggressiveness
        {
            passiv,
            deceive,
            hunting
        }
        Aggressiveness aggressiveness;

        /// <summary>
        /// decide 
        /// </summary>
        /// <param name="ballPos3"></param>
        /// <param name="ballDir3"></param>
        public void RandomAggressiveness(Vector3 ballPos3, Vector3 ballDir3)
        {
            Random random = new Random();
            int newState = random.Next(10+aiStrength);
            if (newState < 4)
                aggressiveness = Aggressiveness.passiv;
            else if (newState < 6)
            {
                aggressiveness = Aggressiveness.hunting;
                CalcTarget(ballPos3, ballDir3);
            }
            else
            {
                aggressiveness = Aggressiveness.deceive;
                Vector3 fakeBallDir;
                if (newState % 2 == 0)
                    fakeBallDir = RotateY(ballDir3, (float)newState / 10f);
                else
                    fakeBallDir = RotateY(ballDir3, -(float)newState / 10f);
                CalcTarget(ballPos3, fakeBallDir);
            }

        }
        
        private void MoveToBall(GameTime gameTime, float distanceDelta)
        {
            float diff = targetRotation - Rotation - distanceDelta;
            float valueDiff = diff;
            if (valueDiff < 0.0f)
                valueDiff = -valueDiff;
            //ball right of AIPaddle if:
            // diff < 0 || diff > PI
            //ball left of AIPaddle if:
            //0 < diff < PI 
            bool enoughSpeed = Delta * Friction * 40.0f > valueDiff;
            if ((diff > 0.03f || diff < -0.03f) && !enoughSpeed)
            {
                if (diff > 0.0f && diff < MathHelper.Pi || diff < -MathHelper.Pi)
                    Delta += Speed * gameTime.ElapsedGameTime.Milliseconds / 1000.0f;
                else
                    Delta -= Speed * gameTime.ElapsedGameTime.Milliseconds / 1000.0f;
            }

            
        }
        public override void Update(GameTime gameTime)
        {
            UpdateEnergy(gameTime);
            float enemyDelta = OtherPaddle.Delta;
            float enemyRotation = OtherPaddle.Rotation;
            #region BallHunting
            //debug-info
            if (enemyDelta > maxDelta)
                maxDelta = enemyDelta;
            if (ballHunting)
            {
                float distance = enemyRotation - Rotation;
                if (distance < 0)
                    distance = -distance;
                //react only if enemy paddle is nearing
                if (distance < lastDistance)
                {
                    float maxDist = 2.5f;
                    //if enemy moves fast towards this paddle or is quite close
                    if ((enemyDelta > 0.01f || enemyDelta < -0.01f) &&
                        (distance < maxDist || distance > MathHelper.TwoPi - maxDist))
                    {
                        distanceDelta += enemyDelta;
                    }
                    else
                        distanceDelta = 0;
                    //else
                    //    if(distanceDelta > 0 || distanceDelta < 0)
                    //        distanceDelta = 0;
                    //if (distanceDelta < minDistDelta)
                    //    minDistDelta = distanceDelta;
                    //else if (distanceDelta > maxDistDelta)
                    //    maxDistDelta = distanceDelta;
                }
                MoveToBall(gameTime, distanceDelta);
                lastDistance = distance;
            }
            #endregion
            #region PaddleHunting
            //... i really like this name^^
            else
            {
                switch (aggressiveness)
                {
                        //pretend to ram the enemy but don't do so
                    case Aggressiveness.deceive:
                        MoveToBall(gameTime, -enemyDelta);
                        break;
                        //ram the enemy (nothing other than trying to catch the ball)
                    case Aggressiveness.hunting:
                        MoveToBall(gameTime, 0);
                        break;
                        //stay in place
                    case Aggressiveness.passiv:
                        break;
                }
            }
            #endregion

            Delta *= Friction;
            Rotation += Delta;
            if (Rotation >= MathHelper.TwoPi)
                Rotation -= MathHelper.TwoPi;
            else if (Rotation < 0.0f)
                Rotation += MathHelper.TwoPi;

            basicModel.World = Matrix.CreateScale(scale);
            basicModel.World *= Matrix.CreateRotationX(-MathHelper.PiOver2);
            basicModel.World *= Matrix.CreateTranslation(0.0f, 0.0f, InnerRadius);
            basicModel.World *= Matrix.CreateRotationY(Rotation);

            paddleContent.Update(gameTime);
        }

        /// <summary>
        /// calculates where ball hits the InnerRing
        /// </summary>
        /// <param name="ballPos"></param>
        /// <param name="ballDir"></param>
        public void CalcTarget(Vector3 ballPos3, Vector3 ballDir3)
        {
            //ballHunting = true;
            Vector2 ballPos = new Vector2(ballPos3.X, ballPos3.Z);
            Vector2 ballDir = new Vector2(ballDir3.X, ballDir3.Z);
            
            float dot = Vector2.Dot(ballDir, ballPos);
            dot = -dot;
            if (dot < 0)
                dot = -dot;
            //calc cut-point of ball with ring
            Vector2 cutPoint = ballPos + ballDir * dot;
            float length2 = cutPoint.Length();
            float length3 = (float)Math.Sqrt(InnerRadius * InnerRadius - length2 * length2);
            length3 += dot;
            Vector2 targetPosition = ballPos + ballDir * length3;
            targetRotation = -(float)Math.Acos((double)Vector2.Dot(targetPosition, new Vector2(0.0f, 1.0f)) /
                (double)targetPosition.Length());
            if (targetPosition.X > 0.0f)
                targetRotation = MathHelper.TwoPi - targetRotation;
            if (targetRotation > MathHelper.TwoPi)
                targetRotation -= MathHelper.TwoPi;
            else if (targetRotation < 0)
                targetRotation += MathHelper.TwoPi;
        }
        public override void Reset()
        {
            base.Reset();
            //move target a little bit so ball won't just hit in 90 degree in case of aiPlayer
            //is starting
            Random random = new Random();
            if(random.Next()%2 == 0)
                targetRotation = MathHelper.Lerp(0.05f, 0.2f, (float)random.NextDouble());            
            else
                targetRotation = MathHelper.Lerp(-0.05f, -0.2f, (float)random.NextDouble());
            ballHunting = true;
        }
        
        #endregion
    }
}
